package com.googlecode.restitory.gae.hook;

import java.util.ArrayList;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Transaction;
import com.googlecode.restitory.gae.GAERequestUtil;
import com.googlecode.restitory.gae.Request;
import com.googlecode.restitory.gae.Response;
import com.googlecode.restitory.gae.Type;

public class DocHook implements Hook {

	private static final Logger LOG = LoggerFactory.getLogger(DocHook.class);

	public DatastoreService getDatastoreService() {
		return DatastoreServiceFactory.getDatastoreService();
	}

	private Response post(Request req) {
		String path = req.getPath();
		if (!path.endsWith("/")) {
			path += "/";
		}
		path += UUID.randomUUID().toString();
		Request nreq = req.copy().setPath(path);
		return put(nreq);
	}

	private Response delete(Request req) {
		Transaction tx = getDatastoreService().beginTransaction();
		try {
			Key key = GAERequestUtil.requestToKey(req);
			Key contentKey = GAERequestUtil.toContentKey(key);
			getDatastoreService().delete(contentKey, key);
			tx.commit();
			return new Response(req).setCode(200);
		} finally {
			rollback(tx);
		}
	}

	private Response put(Request req) {
		Transaction tx = getDatastoreService().beginTransaction();
		try {
			ArrayList<Entity> list = new ArrayList<Entity>();
			GAERequestUtil.createEntities(req, list);
			getDatastoreService().put(list);
			tx.commit();
			Response resp = new Response(req);
			GAERequestUtil.writeResponse(resp, req.getPath());
			return resp.setCode(200);
		} finally {
			rollback(tx);
		}
	}

	private void rollback(Transaction tx) {
		try {
			if (tx.isActive()) {
				tx.rollback();
			}
		} catch (Exception e) {
			LOG.error("error on rollback", e);
		}
	}

	private Response get(Request req) {
		Response resp = new Response(req);
		try {
			Key key = GAERequestUtil.requestToKey(req);
			Key contentKey = GAERequestUtil.toContentKey(key);

			Entity entity = getDatastoreService().get(key);
			GAERequestUtil.populateResponse(entity, resp);

			Entity contentEntity = getDatastoreService().get(contentKey);
			GAERequestUtil.populateContentResponse(contentEntity, resp);

			return resp.setCode(200);
		} catch (EntityNotFoundException e) {
			return resp.setCode(404).setMessage("not found");
		}
	}

	@Override
	public Response execute(Request req, Chain chain) {
		Type type = req.getType();
		switch (type) {
		case GET:
			return get(req);
		case PUT:
			return put(req);
		case POST:
			return post(req);
		case DELETE:
			return delete(req);
		}

		throw new RuntimeException("not supported: " + req);
	}
}
